using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
using Engine;
using GameEntitySystem;
using LiteNetLib;
using TemplatesDatabase;
using XmlUtilities;

namespace Game
{
	public class SubsystemRemoteEntities : Subsystem
	{
		private class SyncPlayerEntityIdPacket
		{
			[CompilerGenerated]
			private string _003CPlayerIdMap_003Ek__BackingField;

			public string PlayerIdMap
			{
				[CompilerGenerated]
				get
				{
					return _003CPlayerIdMap_003Ek__BackingField;
				}
				[CompilerGenerated]
				set
				{
					_003CPlayerIdMap_003Ek__BackingField = value;
				}
			}
		}

		private class AddEntityPacket
		{
			[CompilerGenerated]
			private string _003CEntityData_003Ek__BackingField;

			public string EntityData
			{
				[CompilerGenerated]
				get
				{
					return _003CEntityData_003Ek__BackingField;
				}
				[CompilerGenerated]
				set
				{
					_003CEntityData_003Ek__BackingField = value;
				}
			}
		}

		private class RemoveEntityPacket
		{
			[CompilerGenerated]
			private int _003CEntityIndex_003Ek__BackingField;

			public int EntityIndex
			{
				[CompilerGenerated]
				get
				{
					return _003CEntityIndex_003Ek__BackingField;
				}
				[CompilerGenerated]
				set
				{
					_003CEntityIndex_003Ek__BackingField = value;
				}
			}
		}

		[CompilerGenerated]
		private sealed class _003C_003Ec__DisplayClass29_0
		{
			public int index;

			internal bool _003COnSyncPlayerEntityIdPacket_003Eb__0(PlayerData d)
			{
				return d.PlayerIndex == index;
			}
		}

		private SubsystemPlayers players_;

		private SubsystemRemotePlayers remotePlayers_;

		private int nextId_;

		private Dictionary<int, Entity> entityIdMap_ = new Dictionary<int, Entity>();

		private Dictionary<Entity, int> idEntityMap_ = new Dictionary<Entity, int>();

		private Dictionary<int, int> playerEntityIds_ = new Dictionary<int, int>();

		public event Action<int, Entity> EntityRecordAdded;

		public event Action<int> EntityRecordRemoved;

		public int GetEntityId(Entity e)
		{
			if (idEntityMap_.ContainsKey(e))
			{
				return idEntityMap_[e];
			}
			return -1;
		}

		public Entity GetEntity(int id)
		{
			if (entityIdMap_.ContainsKey(id))
			{
				return entityIdMap_[id];
			}
			return null;
		}

		public void SendEntities(NetPeer peer, List<Entity> entities)
		{
			EntityDataList entityDataList = base.Project.SaveEntities(entities);
			for (int i = 0; i < entities.Count; i++)
			{
				entityDataList.EntitiesData[i].Id = idEntityMap_[entities[i]];
			}
			XElement xElement = new XElement("Entities");
			entityDataList.Save(xElement);
			AddEntityPacket addEntityPacket = new AddEntityPacket();
			addEntityPacket.EntityData = XmlUtils.SaveXmlToString(xElement, true);
			NetworkManager.SendPacket(peer, addEntityPacket, DeliveryMethod.ReliableOrdered, NetworkManager.PeerStage.Ready);
		}

		protected override void OnEntityAdded(Entity entity)
		{
			if (remotePlayers_.HasAuthority)
			{
				AddEntityRecord(entity);
				if (entity.FindComponent<ComponentPlayer>() == null)
				{
					EntityDataList entityDataList = base.Project.SaveEntities(new Entity[1] { entity });
					entityDataList.EntitiesData[0].Id = idEntityMap_[entity];
					XElement xElement = new XElement("Entities");
					entityDataList.Save(xElement);
					AddEntityPacket addEntityPacket = new AddEntityPacket();
					addEntityPacket.EntityData = XmlUtils.SaveXmlToString(xElement, true);
					NetworkManager.SendPacketToAll(addEntityPacket, DeliveryMethod.ReliableOrdered, NetworkManager.PeerStage.Ready);
				}
				else
				{
					SyncPlayerEntityId();
				}
			}
			else
			{
				if (idEntityMap_.ContainsKey(entity))
				{
					return;
				}
				ComponentPlayer componentPlayer = entity.FindComponent<ComponentPlayer>();
				if (componentPlayer != null)
				{
					if (!remotePlayers_.IsNewPlayer(componentPlayer.PlayerData.PlayerIndex))
					{
						if (!playerEntityIds_.ContainsKey(componentPlayer.PlayerData.PlayerIndex))
						{
							Log.Information("Cannot find player entity id for " + componentPlayer.PlayerData.Name);
						}
						else
						{
							SetEntityRecord(playerEntityIds_[componentPlayer.PlayerData.PlayerIndex], entity);
						}
					}
				}
				else
				{
					Log.Error("found out of sync entity: " + entity.ValuesDictionary.DatabaseObject.Name);
				}
			}
		}

		protected override void OnEntityRemoved(Entity entity)
		{
			if (remotePlayers_.HasAuthority)
			{
				int num = idEntityMap_[entity];
				RemoveEntityRecord(num);
				RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
				removeEntityPacket.EntityIndex = num;
				NetworkManager.SendPacketToAll(removeEntityPacket, DeliveryMethod.ReliableOrdered, NetworkManager.PeerStage.Ready);
			}
			else if (players_.IsPlayer(entity))
			{
				int id = playerEntityIds_[entity.FindComponent<ComponentPlayer>().PlayerData.PlayerIndex];
				RemoveEntityRecord(id);
			}
			else if (idEntityMap_.ContainsKey(entity))
			{
				Log.Error("found out of sync entity: " + entity.ValuesDictionary.DatabaseObject.Name);
			}
		}

		protected override void Load(ValuesDictionary valuesDictionary)
		{
			players_ = base.Project.FindSubsystem<SubsystemPlayers>(true);
			remotePlayers_ = base.Project.FindSubsystem<SubsystemRemotePlayers>(true);
			if (!remotePlayers_.HasAuthority)
			{
				ValuesDictionary value = valuesDictionary.GetValue<ValuesDictionary>("PlayerEntityIds", null);
				if (value != null)
				{
					foreach (KeyValuePair<string, object> item in value)
					{
						playerEntityIds_[int.Parse(item.Key)] = (int)item.Value;
					}
				}
			}
			NetworkManager.SubscribeReusablePacket(new Action<AddEntityPacket, NetPeer>(OnReciveAddEntity));
			NetworkManager.SubscribeReusablePacket(new Action<RemoveEntityPacket, NetPeer>(OnReciveRemoveEntity));
			NetworkManager.SubscribeReusablePacket(new Action<SyncPlayerEntityIdPacket, NetPeer>(OnSyncPlayerEntityIdPacket));
		}

		protected override void Save(ValuesDictionary valuesDictionary)
		{
			ValuesDictionary valuesDictionary2 = new ValuesDictionary();
			foreach (KeyValuePair<Entity, int> item in idEntityMap_)
			{
				if (players_.IsPlayer(item.Key))
				{
					valuesDictionary2.Add(item.Key.FindComponent<ComponentPlayer>().PlayerData.PlayerIndex.ToString(), item.Value);
				}
			}
			valuesDictionary.Add("PlayerEntityIds", valuesDictionary2);
		}

		public override void Dispose()
		{
			NetworkManager.RemoveSubscribtion<AddEntityPacket>();
			NetworkManager.RemoveSubscribtion<RemoveEntityPacket>();
			NetworkManager.RemoveSubscribtion<SyncPlayerEntityIdPacket>();
		}

		private void SetEntityRecord(int id, Entity entity)
		{
			entityIdMap_[id] = entity;
			idEntityMap_[entity] = id;
			Action<int, Entity> action = this.EntityRecordAdded;
			if (action != null)
			{
				action(id, entity);
			}
		}

		private void AddEntityRecord(Entity entity)
		{
			SetEntityRecord(nextId_, entity);
			nextId_++;
		}

		private void RemoveEntityRecord(int id)
		{
			idEntityMap_.Remove(entityIdMap_[id]);
			entityIdMap_.Remove(id);
			Action<int> action = this.EntityRecordRemoved;
			if (action != null)
			{
				action(id);
			}
		}

		private void OnReciveAddEntity(AddEntityPacket packet, NetPeer peer)
		{
			XElement entitiesNode = XmlUtils.LoadXmlFromString(packet.EntityData, true);
			EntityDataList entityDataList = new EntityDataList(DatabaseManager.GameDatabase, entitiesNode, false);
			List<Entity> list = base.Project.LoadEntities(entityDataList);
			for (int i = 0; i < list.Count; i++)
			{
				SetEntityRecord(entityDataList.EntitiesData[i].Id, list[i]);
			}
			base.Project.AddEntities(list);
		}

		private void OnReciveRemoveEntity(RemoveEntityPacket packet, NetPeer peer)
		{
			Entity entity = entityIdMap_[packet.EntityIndex];
			RemoveEntityRecord(packet.EntityIndex);
			base.Project.RemoveEntity(entity, true);
		}

		private void SyncPlayerEntityId()
		{
			ValuesDictionary valuesDictionary = new ValuesDictionary();
			if (remotePlayers_.HasAuthority)
			{
				foreach (PlayerData playersDatum in players_.PlayersData)
				{
					if (playersDatum.ComponentPlayer != null)
					{
						valuesDictionary.Add(playersDatum.PlayerIndex.ToString(), GetEntityId(playersDatum.ComponentPlayer.Entity));
					}
				}
			}
			XElement node = new XElement("PlayerEntityIds");
			valuesDictionary.Save(node);
			SyncPlayerEntityIdPacket syncPlayerEntityIdPacket = new SyncPlayerEntityIdPacket();
			syncPlayerEntityIdPacket.PlayerIdMap = XmlUtils.SaveXmlToString(node, true);
			NetworkManager.SendPacketToAll(syncPlayerEntityIdPacket, DeliveryMethod.ReliableOrdered, NetworkManager.PeerStage.Ready);
		}

		private void OnSyncPlayerEntityIdPacket(SyncPlayerEntityIdPacket packet, NetPeer peer)
		{
			XElement overridesNode = XmlUtils.LoadXmlFromString(packet.PlayerIdMap, true);
			ValuesDictionary valuesDictionary = new ValuesDictionary();
			valuesDictionary.ApplyOverrides(overridesNode);
			foreach (KeyValuePair<string, object> item in valuesDictionary)
			{
				_003C_003Ec__DisplayClass29_0 _003C_003Ec__DisplayClass29_ = new _003C_003Ec__DisplayClass29_0();
				_003C_003Ec__DisplayClass29_.index = int.Parse(item.Key);
				PlayerData playerData = Enumerable.FirstOrDefault(players_.PlayersData, new Func<PlayerData, bool>(_003C_003Ec__DisplayClass29_._003COnSyncPlayerEntityIdPacket_003Eb__0));
				if (playerData != null && playerData.ComponentPlayer != null)
				{
					SetEntityRecord((int)item.Value, playerData.ComponentPlayer.Entity);
				}
			}
		}
	}
}
